/**
 * 	 DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *   @author Colin Redmond
 *   Copyright (C) 2011 Colin Redmond
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */

package com.cred.industries.platform.business.objects;

import java.util.HashMap;
import java.util.Map;

import javax.ws.rs.core.Response.Status;

import org.apache.commons.validator.routines.*;

import com.cred.industries.core.exceptions.ApplicationException;
import com.cred.industries.core.server.ServerConfig;
import com.cred.industries.core.server.ServerConfigEnum;
import com.cred.industries.core.transferObj.result.ReturnCodes;
import com.cred.industries.platform.business.security.Authorization;
import com.cred.industries.platform.dao.CustomerDAOFactory;
import com.cred.industries.platform.dao.CustomerRolesDAOFactory;
import com.cred.industries.platform.dao.ICustomerDAO;
import com.cred.industries.platform.dao.ICustomerRolesDAO;
import com.cred.industries.platform.defines.Role;
import com.cred.industries.platform.session.SessionData;

/**
 * All business rules relating to the Customer
 * and all the data needed to modify the customer in the DB
 */
public class CustomerBO implements Authorization {
	
	public enum fields 
	{	
		CUSTOMER_ID("customerId"),
		PERSONA_NAME("personaName"),
		PASSWORD("password"),
		FIRST_NAME("firstName"),
		LAST_NAME("lastname"),
		EMAIL_ADDRESS("emailAddress");	
	
		private String mFieldName;
		fields(String fieldName) {
			
			mFieldName = fieldName; 
		}
		
		public String toString() {
			return mFieldName;
		}
	};
	
	private int mCustomerId = -1;
	private String mPersonaName;
	private String mPassword;
	private String mFirstName;
	private String mLastName;
	private String mEmailAddress;
	private CustomerRolesBO mRoles;
	private Map<fields, String> mValidationErrors;
	

	/**
	 * default constructor
	 */
	public CustomerBO() {
		
		this.mPersonaName = "";
		this.mPassword = "";
		this.mFirstName = "";
		this.mLastName = "";
		this.mEmailAddress = "";
		this.mValidationErrors = new HashMap<>();
	}
	/**
	 * Constructor to populate all required memver variables
	 * @param customerId
	 * @param personaName
	 * @param password
	 * @param firstName
	 * @param lastName
	 * @param emailAddress
	 */
	
	public CustomerBO(int customerId, String personaName, String password, String firstName, String lastName, String emailAddress) {
		
		this.mCustomerId = customerId;
		this.mPersonaName = personaName;
		this.mPassword = password;
		this.mFirstName = firstName;
		this.mLastName = lastName;
		this.mEmailAddress = emailAddress;
		this.mValidationErrors = new HashMap<>();
	}
	
	public int getCustomerId() { return mCustomerId;	}
	public void setCustomerId(int customerId) {	this.mCustomerId = customerId; }
	public void setCustomerId(String customerId) {	this.mCustomerId = Integer.parseInt(customerId); }
	public String getPersonaName() { return mPersonaName; }
	public void setPersonaName(String personaName) { this.mPersonaName = personaName; }
	public String getPassword() { return mPassword; }
	public void setPassword(String password) { this.mPassword = password; }
	public String getFirstName() { return mFirstName; }
	public void setFirstName(String firstName) { this.mFirstName = firstName; }
	public String getLastName() { return mLastName; }
	public void setLastName(String lastName) { this.mLastName = lastName; }
	public String getEmailAddress() { return mEmailAddress; }
	public void setEmailAddress(String emailAddress) { this.mEmailAddress = emailAddress; }
	
	/**
	 * 
	 * @param field the field we want to check for errors
	 * @return any errors for a given field
	 */
	public String getValidationError(fields field) {
		
		String errorMsg = mValidationErrors.get(field);
	    return (errorMsg == null) ? "":errorMsg;
	}
	
	/**
	 * 
	 * @return a comma separated list of errors
	 */
	public String getValidationErrors() {
		
		StringBuilder errorMsg = new StringBuilder();
		
		//build a comma separated list of errors
		for(String i:mValidationErrors.values()) {
			if(errorMsg.length() > 0) {
				errorMsg.append(",");
			} 
			errorMsg.append(i);
		}
		
		return errorMsg.toString();
	}

	
	/**
	 * we use lazy initialization for the roles. We don't get them unless 
	 * we are asked for them.
	 * @return the roles for the customer
	 * @throws ApplicationException
	 */
	public CustomerRolesBO getRoles() {
		if(mRoles == null) {
			ICustomerRolesDAO custRolesDAO = CustomerRolesDAOFactory.create();
			CustomerRolesBO tempRoles = custRolesDAO.findCustomerRoleByPersonaName(mPersonaName);
			 
			if(tempRoles == null || tempRoles.getRoles().size() == 0) {
				//if we dont have any roles, then we are not authorized.
				throw new ApplicationException(Status.UNAUTHORIZED, ReturnCodes.PERSONA_HAS_NO_ROLES, "unable to find roles for " + mCustomerId);
			}
			mRoles = tempRoles;
		}
		return mRoles;
	}

	/**
	 * Validates all fields in the customer. Creates a list of errors for each invalid field
	 * @return if we have a valid customer
	 * @throws IllegalArgumentException
	 */
	public boolean validateCustomer() {
		//validate the business rules for the customer
		mValidationErrors.clear();
		//validate persona name
		if(mPersonaName == null || mPersonaName.length() == 0) {
			mValidationErrors.put(fields.PERSONA_NAME, ReturnCodes.PERSONA_NAME_CANT_BE_EMPTY.toString());
		} else if(ServerConfig.getInstance().hasValue(Integer.class, ServerConfigEnum.MIN_NAME_LENGHT)  
				&& mPersonaName.length() < ServerConfig.getInstance().getValue(Integer.class,ServerConfigEnum.MIN_NAME_LENGHT)) {
			mValidationErrors.put(fields.PERSONA_NAME, ReturnCodes.PERSONA_NAME_TOO_SHORT.toString());
		} else if(ServerConfig.getInstance().hasValue(Integer.class,ServerConfigEnum.MAX_NAME_LENGHT)  
				&& mPersonaName.length() > ServerConfig.getInstance().getValue(Integer.class,ServerConfigEnum.MAX_NAME_LENGHT)) {
			mValidationErrors.put(fields.PERSONA_NAME, ReturnCodes.PERSONA_NAME_TOO_LONG.toString());
		} else {
			ICustomerDAO custDAO = CustomerDAOFactory.create();
			CustomerBO custBO = custDAO.findCustomerByPersonaName(mPersonaName);
			//verify that this persona name isn't taken, or that it is the current customer we want to change
			if(custBO != null && custBO.mCustomerId != this.mCustomerId) {
				mValidationErrors.put(fields.PERSONA_NAME, ReturnCodes.PERSONA_NAME_ALREADY_TAKEN.toString());
			}
		}
		
		//validate password
		if(mPassword == null || ServerConfig.getInstance().hasValue(Integer.class,ServerConfigEnum.MIN_PASSWORD_LENGHT) 
				&& mPassword.length() < ServerConfig.getInstance().getValue(Integer.class,ServerConfigEnum.MIN_PASSWORD_LENGHT)) {
			mValidationErrors.put(fields.PASSWORD, ReturnCodes.PASSWORD_TO_SHORT.toString());
		} else if(ServerConfig.getInstance().hasValue(Integer.class,ServerConfigEnum.MAX_PASSWORD_LENGHT) 
				&& mPassword.length() > ServerConfig.getInstance().getValue(Integer.class,ServerConfigEnum.MAX_PASSWORD_LENGHT)) {
			mValidationErrors.put(fields.PASSWORD, ReturnCodes.PASSWORD_TO_LONG.toString());
		}
		
		//validate first name
		if(mFirstName == null || mFirstName.length() == 0) {
			mValidationErrors.put(fields.FIRST_NAME, ReturnCodes.FIRST_NAME_CANT_BE_EMPTY.toString());
		} else if(ServerConfig.getInstance().hasValue(Integer.class,ServerConfigEnum.MAX_NAME_LENGHT) 
				&& mFirstName.length() > ServerConfig.getInstance().getValue(Integer.class,ServerConfigEnum.MAX_NAME_LENGHT)) {
			mValidationErrors.put(fields.FIRST_NAME, ReturnCodes.FIRST_NAME_TOO_LONG.toString());
		}
		
		//validate last name
		if(mLastName == null || mLastName.length() == 0) {
			mValidationErrors.put(fields.LAST_NAME, ReturnCodes.LAST_NAME_CANT_BE_EMPTY.toString());
		} else if(ServerConfig.getInstance().hasValue(Integer.class,ServerConfigEnum.MAX_NAME_LENGHT)  
				&& mLastName.length() > ServerConfig.getInstance().getValue(Integer.class,ServerConfigEnum.MAX_NAME_LENGHT)) {
			mValidationErrors.put(fields.LAST_NAME, ReturnCodes.LAST_NAME_TOO_LONG.toString());
		}
		
		//validate email address name
		if(mEmailAddress == null|| mEmailAddress.length() == 0) {
			mValidationErrors.put(fields.EMAIL_ADDRESS, ReturnCodes.EMAIL_CANT_BE_EMPTY.toString());
		} else if(ServerConfig.getInstance().hasValue(Integer.class,ServerConfigEnum.MAX_EMAIL_LENGHT) 
				&& mEmailAddress.length() > ServerConfig.getInstance().getValue(Integer.class,ServerConfigEnum.MAX_EMAIL_LENGHT)) {
			mValidationErrors.put(fields.EMAIL_ADDRESS, ReturnCodes.EMAIL_ADDRESS_TOO_LONG.toString());
		} else if(!EmailValidator.getInstance().isValid(mEmailAddress)) {
			mValidationErrors.put(fields.EMAIL_ADDRESS, ReturnCodes.INVALID_EMAIL_ADDRESS.toString());
		}
		
		return mValidationErrors.isEmpty();
	}
	
	/**
	 * utility function to see if this customer is the authenticated user
	 * @return true if this is the authenticated user
	 */
	public boolean isAuthenticatedUser() {
		CustomerBO authenticatedUser = SessionData.getSessionCustomer();
		
		return authenticatedUser != null && mCustomerId == authenticatedUser.getCustomerId();
	}
	
	/**
	 * returns if the currently authenticated customer is authorized to modify
	 * this customer BO. 
	 * a CS, admin or yourself can make changes to this BO
	 * @return true if this is a CS, admin or yourself
	 */
	@Override
	public boolean authorizedToModify() {
		
		//a CS, admin or yourself can make changes to this BO
		CustomerBO authenticatedUser = SessionData.getSessionCustomer();
		CustomerRolesBO roles = authenticatedUser.getRoles();
		return authenticatedUser != null 
				&& (roles.hasRole(Role.ADMIN) 
				|| roles.hasRole(Role.CS) 
				|| mCustomerId == authenticatedUser.getCustomerId());
	}
	
	/**
	 * anyone can access this BO
	 * @return true
	 */
	@Override
	public boolean authorizedToAccess() {
		
		//any one can read this BO
		return true;
	}
	
	/**
	 * returns if the currently authenticated customer is a super user
	 * Ie a user with permissions to change anything related to this BO
	 * In this case a CS or admin admin
	 * @return true if this is a CS, or admin.
	 */
	@Override
	public boolean authorizedAsSuperuser() {
		
		//a CS, admin or yourself can make changes to this BO
		CustomerBO authenticatedUser = SessionData.getSessionCustomer();
		CustomerRolesBO roles = authenticatedUser.getRoles();
		return authenticatedUser != null
				&& (roles.hasRole(Role.ADMIN)
				||  roles.hasRole(Role.CS));
	}
	
}
